package com.xunk.equipment.service.acrel.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.xunk.common.core.constant.CacheConstants;
import com.xunk.common.core.utils.DateUtils;
import com.xunk.common.redis.service.RedisService;
import com.xunk.equipment.domain.EquipmentDO;
import com.xunk.equipment.influxdb.InfluxCustomMapper;
import com.xunk.equipment.influxdb.entity.HeatFlowmeterEntity;
import com.xunk.equipment.mapper.EquipmentMapper;
import com.xunk.equipment.service.acrel.constant.FlowmeterUnitEnum;
import com.xunk.equipment.service.acrel.service.AcrelSmartService;
import com.xunk.equipment.service.acrel.service.PublishMessageComponent;
import com.xunk.equipment.service.acrel.vo.AcrelFlowmeterDetailVO;
import com.xunk.equipment.service.acrel.vo.AcrelHeatFlowmeterVO;
import com.xunk.equipment.service.acrel.vo.SmartHeatVO;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.dto.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.xunk.common.core.domain.EquipmentResponse;

/**
 * @ProjectName: xunk
 * @Package: com.xunk.equipment.service.acrel.service.impl
 * @ClassName: SmartHeatServiceImpl
 * @Author: dongxiaoxin
 * @Description:
 * @Date: 2021/3/29 15:48
 * @Version: 1.0
 */
@Slf4j
@Service("acrel-heat-meter")
public class SmartHeatServiceImpl extends PublishMessageComponent implements AcrelSmartService {

    @Autowired
    private RedisService service;

    @Autowired
    private InfluxCustomMapper mapper;

    @Autowired
    private EquipmentMapper equipmentMapper;

    private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

    @Override
    @SuppressWarnings("unchecked")
    public <T> List<T> queryFlowmeter(Long homeId, String unit) throws ParseException {

        if (homeId != 8) {
            if (FlowmeterUnitEnum.getDefault().equals(unit)) {
                return (List<T>) AutoDayData();
            } else if ("month".equals(unit)) {
                return (List<T>) AutoData();
            } else if ("season".equals(unit)) {
                return (List<T>) AutoSeacsonData();
            } else if ("halfYear".equals(unit)) {
                return (List<T>) AutoHalfYear();
            } else {
                return (List<T>) AutoYear();
            }
        } else {
            String querySql = String.format("SELECT * FROM \"heat_tb\" WHERE time > now() - %s and home_id = '%s'",
                    FlowmeterUnitEnum.parseDeviceEnum(unit).getTime(), homeId);
            List<HeatFlowmeterEntity> list = mapper.query(new Query(querySql, "paramter_db"), HeatFlowmeterEntity.class);
            log.info("今天NOW" + LocalDateTime.now().toString() + "趋势图数据详细信息 —> 时间单位：" + unit + "，SQl：" + querySql + ",查询结果：" + list.size());
            if (CollectionUtils.isEmpty(list)) {
                return new ArrayList<>();
            }
            List<SmartHeatVO> resList = new ArrayList<>();
            if (FlowmeterUnitEnum.getDefault().equals(unit)) {
                for (HeatFlowmeterEntity entity : list) {
                    String time = entity.getTime().toString();
                    Date parse = df.parse(time);
                    //  查询最近一天的
                    //  排除不是今天的数据
                    if (Integer.parseInt(DateUtils.parseDateToStr("dd", parse)) != LocalDate.now().getDayOfMonth()) {
                        continue;
                    }
                    if (parse.getHours() % 2 == 0) {
                        resList.add(new SmartHeatVO(parse.getHours() + "", new BigDecimal(entity.getGswd()).setScale(2, RoundingMode.HALF_UP).toString(),
                                new BigDecimal(entity.getHswd()).setScale(2, RoundingMode.HALF_UP).toString()));
                    }
                }
                return (List<T>) resList;
            } else {
                return (List<T>) dealMeterByUnit(unit, list);
            }
        }
    }

    private List<SmartHeatVO> dealMeterByUnit(String unit, List<HeatFlowmeterEntity> list) throws ParseException {

        FlowmeterUnitEnum unitEnum = FlowmeterUnitEnum.parseDeviceEnum(unit);
        List<SmartHeatVO> resList = new ArrayList<>();
        Map<Integer, List<SmartHeatVO>> map = new HashMap<>();
        String weekDay = DateUtils.getWeekDayString(df.parse(list.get(0).getTime().toString()));
        for (HeatFlowmeterEntity entity : list) {

            Date parse;
            try {
                parse = df.parse(entity.getTime().toString());
            } catch (Exception e) {
                log.error("时间格式化失败：{}", entity.getTime());
                continue;
            }
            int dateKey = 0;
            switch (unitEnum) {
                case LAST_MONTH:
                    //  最近一月的
                    //  排除不是当月的数据
                    if (parse.getMonth() + 1 != LocalDate.now().getMonth().getValue()) {
                        continue;
                    }
                    dateKey = Integer.parseInt(DateUtils.parseDateToStr("dd", parse));
                    if (dateKey % 5 == 0) {
                        continue;
                    }
                    break;
                case LAST_SEASON:
                    //  最近三个月
                    if (weekDay.equals(DateUtils.getWeekDayString(parse)) && (parseDate("mm", parse) == 0 || list.get(0).getTime().equals(entity.getTime())))
                        dateKey = Integer.parseInt(DateUtils.parseDateToStr("MMdd", parse));
                    break;
                case LAST_HALF_YEAR:
                    //  最近半年
                    if (Integer.parseInt(DateUtils.parseDateToStr("yyyy", parse)) != LocalDate.now().getYear()) {
                        continue;
                    }
                    dateKey = Integer.parseInt(DateUtils.parseDateToStr("MM", parse));
                    break;
                case LAST_YEAR:
                    //  最近一年
                    if (Integer.parseInt(DateUtils.parseDateToStr("yyyy", parse)) != LocalDate.now().getYear()) {
                        continue;
                    }
                    dateKey = Integer.parseInt(DateUtils.parseDateToStr("MM", parse));
                    break;
                default:
                    throw new RuntimeException("查询时间单位类型错误");
            }
            if (dateKey == 0) {
                continue;
            }
            List<SmartHeatVO> heats = map.get(dateKey);
            if (CollectionUtils.isEmpty(heats)) {
                heats = new ArrayList<>();
            }
            heats.add(new SmartHeatVO(entity.getGswd(), entity.getHswd()));
            map.put(dateKey, heats);
        }
        for (Integer key : map.keySet()) {
            List<SmartHeatVO> vos = map.get(key);
            BigDecimal hswd = new BigDecimal("0.0");
            BigDecimal gswd = new BigDecimal("0.0");
            for (SmartHeatVO vo : vos) {
                hswd = hswd.add(new BigDecimal(vo.getHswd()));
                gswd = gswd.add(new BigDecimal(vo.getGswd()));
            }
            resList.add(new SmartHeatVO(key + "", hswd.divide(new BigDecimal(vos.size()), 2, BigDecimal.ROUND_HALF_UP).toString(),
                    gswd.divide(new BigDecimal(vos.size()), 2, BigDecimal.ROUND_HALF_UP).toString()));
        }
        resList.sort(Comparator.comparingInt(x -> Integer.parseInt(x.getDimension())));
        return resList;
    }

    @Override
    public void saveInfluxData(String gateId, AcrelFlowmeterDetailVO<?> vo, String time) {

        AcrelHeatFlowmeterVO object = JSONObject.parseObject(vo.getValues().toString(), AcrelHeatFlowmeterVO.class);
        Long homeId = equipmentMapper.selectHomeIdByDeviceSn(gateId + ":" + vo.getId());
        if (homeId == null) {
            throw new RuntimeException("设备家庭归属未定义");
        }
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(DateUtils.parseDate(time));
        //  整点插入 - 时序数据库
        if (gc.get(GregorianCalendar.MINUTE) == 0) {
            //  存入时序数据库
            mapper.save(new HeatFlowmeterEntity(homeId + "", object.getGswd(), object.getHswd(), Instant.ofEpochMilli(DateUtils.addHours(new Date(), 8).getTime())));
        }

        //  将实时的供水温度，回水温度添加到redis中
        //  回水温度
        service.setCacheObject(CacheConstants.CURRENT_WATER_RETURN_TEMP + homeId, Float.parseFloat(object.getHswd()));
        //  供水温度
        service.setCacheObject(CacheConstants.CURRENT_WATER_SUPPLY_TEMP + homeId, Float.parseFloat(object.getGswd()));
        //  当前用热功率
        service.setCacheObject(CacheConstants.CURRENT_HEAT_POWER + homeId, Float.parseFloat(object.getRgl()));
        //  当前瞬时流量
        service.setCacheObject(CacheConstants.CURRENT_HEAT_METER + homeId, Float.parseFloat(object.getSsll()));
        //  设备在线状态
        equipmentMapper.updateEquipmentOnline(gateId + ":" + vo.getId(), Long.parseLong(vo.getStatus()));
    }

    @Override
    public boolean queryOnlineInfo(String deviceSn) {
        return ONLINE_STATUS.equals(equipmentMapper.selectOnlineByDeviceSn(deviceSn));
    }

    @Override
    public void saveAlarmData(String gateId, AcrelFlowmeterDetailVO<?> vo) {
        this.saveAlarmInfo(gateId + ":" + vo.getId(), "尊敬的用户，您的家庭“%s”中‘%s’因检测到漏水而导致自动关闭地暖，请及时寻求专业人员进行检修", "地暖管线漏水通知");
    }

    private int parseDate(String format, Date date) {
        return Integer.parseInt(DateUtils.parseDateToStr(format, date));
    }

    @Override
    public EquipmentResponse getEquipmentInfo(EquipmentDO equipmentDO) {

        Long homeId = this.equipmentMapper.selectHomeIdByDeviceSn(equipmentDO.getDeviceSn());
        SmartHeatVO response = null;
        if (switchKey.contains(equipmentDO.getDeviceSn())) {

            response = new SmartHeatVO(service.getCacheObject(CacheConstants.HEAT_SWITCH_STATUS + homeId),
                    0.0f, 0.0f, "", "21.01", "18.99", "");
        } else {

            response = new SmartHeatVO(service.getCacheObject(CacheConstants.HEAT_SWITCH_STATUS + homeId),
                    service.getCacheObject(CacheConstants.CURRENT_HEAT_POWER + homeId),
                    service.getCacheObject(CacheConstants.CURRENT_HEAT_METER + homeId), "",
                    service.getCacheObject(CacheConstants.CURRENT_WATER_SUPPLY_TEMP + homeId) + "",
                    service.getCacheObject(CacheConstants.CURRENT_WATER_RETURN_TEMP + homeId) + "", "");
        }

        response.setSupplier(equipmentDO.getSupplier());
        response.setDeviceSn(equipmentDO.getDeviceSn());
        response.setCandidateModel(baseService.getCandidateModel(equipmentDO.getDeviceBaseId()));
        return response;
    }

    @Override
    public Boolean control(EquipmentDO equipmentDO, String type, String value) {

        log.info("Special Virtual DeviceSn : {}", JSONObject.toJSONString(switchKey));

        String cacheKey = CacheConstants.HEAT_SWITCH_STATUS;
        if (switchKey.contains(equipmentDO.getDeviceSn())) {
            this.virtualDevice(equipmentDO.getDeviceSn(), value, cacheKey);
        }

        Long homeId = this.publishMessage(equipmentDO, Boolean.parseBoolean(value), type);

        //  记录当前状态（操作之前的开关状态）
        service.setCacheObject(cacheKey + ":last:" + homeId, service.getCacheObject(cacheKey + homeId));
        return true;
    }

    private List<SmartHeatVO> AutoDayData() {

        List<SmartHeatVO> list = new ArrayList<>();
        String hour = DateUtils.dateTimeNow("HH");
        list.add(new SmartHeatVO(hour, "22.74", "21.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 2)), "22.77", "20.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 4)), "22.64", "22.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 6)), "22.77", "21.92"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 8)), "22.45", "21.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 10)), "22.76", "21.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 12)), "22.34", "21.72"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 14)), "25.73", "22.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 16)), "22.77", "21.42"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 18)), "22.34", "21.42"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 20)), "22.73", "21.62"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("HH", DateUtils.addHours(new Date(), 22)), "21.76", "21.92"));
        return list;
    }


    private List<SmartHeatVO> AutoData() {

        List<SmartHeatVO> list = new ArrayList<>();
        Date lastMonth = DateUtils.addMonths(new Date(), -1);

        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 0)), "21.77", "21.32"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 5)), "21.12", "21.31"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 10)), "20.17", "21.22"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 15)), "21.77", "21.52"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 20)), "20.22", "17.22"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 25)), "18.98", "19.37"));
        list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM-dd", DateUtils.addDays(lastMonth, 30)), "18.42", "19.27"));
        return list;
    }

    private List<SmartHeatVO> AutoSeacsonData() {

        String cacheKey = "virtual:heat:season";
        Object object = service.getCacheObject(cacheKey);
        if (object != null) {
            return JSONObject.parseArray(object.toString(), SmartHeatVO.class);
        }
        String[] gswd = {"21.77", "20.13", "18.12", "22.03", "21.91", "18.62", "19.91", "22.42"};
        List<SmartHeatVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -3);

        for (int index = 0; index <= 3 * 30; index++) {
            if (index % 7 == 0) {
                list.add(new SmartHeatVO(DateUtils.parseDateToStr("MM月dd日", DateUtils.addDays(date, index)), gswd[(int) (Math.random() * 7)], gswd[(int) (Math.random() * 7)]));
            }
        }
        service.setCacheObject(cacheKey, list, 1, TimeUnit.DAYS);
        return list;
    }

    private List<SmartHeatVO> AutoHalfYear() {

        Map<String, String> map = initMonthDataMap();
        List<SmartHeatVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -6);

        for (int index = 0; index <= 5; index++) {
            String month = DateUtils.parseDateToStr("M月", DateUtils.addMonths(date, index));
            String[] res = map.get(month).split("-");
            list.add(new SmartHeatVO(month, res[0], res[1]));
        }
        return list;
    }

    private List<SmartHeatVO> AutoYear() {

        Map<String, String> map = initMonthDataMap();
        List<SmartHeatVO> list = new ArrayList<>();
        Date date = DateUtils.addMonths(new Date(), -12);

        for (int index = 0; index <= 11; index++) {
            String month = DateUtils.parseDateToStr("M月", DateUtils.addMonths(date, index));
            String[] res = map.get(month).split("-");
            list.add(new SmartHeatVO(month, res[0], res[1]));
        }
        return list;
    }

    private Map<String, String> initMonthDataMap() {

        String cacheKey = "acrel:heat:virtual:data";
        Map<String, String> heatTotalMap = service.getCacheObject(cacheKey);
        if (heatTotalMap == null) {

            heatTotalMap = new HashMap<>();
            final BigDecimal min = new BigDecimal("18");
            final BigDecimal max = new BigDecimal("23");
            //  初始化Map
            for (int index = 1; index <= 12; index++) {
                heatTotalMap.put(index + "月", RandomUtil.randomBigDecimal(min, max).setScale(2, BigDecimal.ROUND_CEILING).toString() + "-" + RandomUtil.randomBigDecimal(min, max).setScale(2, BigDecimal.ROUND_CEILING).toString());
            }
            service.setCacheObject(cacheKey, heatTotalMap);
        }
        return heatTotalMap;
    }
}
